﻿using System.Collections.Generic;
using Calimbra.Runtime.Contracts;
using Calimbra.Runtime.Data;
using Calimbra.Runtime.Functions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Calimbra.Runtime;

namespace Calimbra.Runtime.Test
{
    
    

    [TestClass()]
    public class FlowRuntimeTest
    {

        [TestMethod()]
        public void No_processing_just_redirect_input_to_output()
        {
            var target = new FlowRuntime();
            target.AddStream(new Stream(".in", ".out"));
            IMessage result = null;
            target.Result += output => result = output;

            target.Process(new Message(".in", "Test message"));

            Assert.AreEqual(".out", result.Port.Fullname);
            Assert.AreEqual("Test message", result.Data);
        }



        [TestMethod()]
        public void TriggerTest()
        {
            var target = new FlowRuntime();
            target.AddStream(new Stream(".in", "Trigger.Input"));
            target.AddStream(new Stream(".in", "Trigger.Fire"));
            target.AddStream(new Stream("Trigger.out", ".out"));

            var trigger = new Trigger<string>();
            target.AddOperation(new Operation("Trigger.Input", trigger.GetAdapter("Input")));
            target.AddOperation(new Operation("Trigger.Fire", trigger.GetAdapter("Fire")));
        
            IMessage result = null;
            target.Result += output => result = output;

            target.Process(new Message(".in", "Test message"));

            Assert.AreEqual(".out", result.Port.Name);
            Assert.AreEqual("Test messageFire", result.Data);
        }


        [TestMethod()]
        public void Trigger_Test()
        {
            var target = new FlowRuntime();
            target.AddStream(new Stream(".in", "Trigger.Fire"));
            target.AddStream(new Stream(".in", "Trigger.Input"));
            target.AddStream(new Stream("Trigger.out", ".out"));

            var trigger = new Trigger<string>();
            target.AddOperation(new Operation("Trigger.Input", trigger.GetAdapter("Input")));
            target.AddOperation(new Operation("Trigger.Fire", trigger.GetAdapter("Fire")));
           
          
            IMessage result = null;
            target.Result += output => result = output;

            target.Process(new Message(".in", "Test message"));

            Assert.AreEqual(".out", result.Port.Name);
            Assert.AreEqual("Test messageInput", result.Data);
        }



        [TestMethod()]
        public void Single_operation()
        {
            var target = new FlowRuntime();
            target.AddStream(new Stream(".in", "A.in"));
            target.AddStream(new Stream("A.out", ".out"));
            target.AddOperation(new Operation("A.in",
                                              (input, outputContinuation) =>
                                              outputContinuation(new Message("A.out", input.Data + "+A"))));
            IMessage result = null;
            target.Result += output => result = output;

            target.Process(new Message(".in", "Test message"));

            Assert.AreEqual(".out", result.Port.Name);
            Assert.AreEqual("Test message+A", result.Data);
        }

        [TestMethod()]
        public void Sequence_of_two_operations()
        {
            var target = new FlowRuntime();

            target.AddStream(new Stream(".in", "A.in"));
            target.AddStream(new Stream("A.out", "B.in"));
            target.AddStream(new Stream("B.out", ".out"));

            target.AddOperation(new Operation("A.in", (input, outputCont) => outputCont(new Message("A.out", input.Data.ToString() + "+A"))));
            target.AddOperation(new Operation("B.in", (input, outputCont) => outputCont(new Message("B.out", input.Data.ToString() + "+B"))));

            IMessage result = null;
            target.Result += output => result = output;

            target.Process(new Message(".in", "Test message"));

            Assert.AreEqual(".out", result.Port.Name);
            Assert.AreEqual("Test message+A+B", result.Data.ToString());
        }

        [TestMethod()]
        public void Sequence_of_three_operations()
        {
            var target = new FlowRuntime();

            target.AddStream(new Stream(".in", "A.in"));
            target.AddStream(new Stream("A.out", "B.in"));
            target.AddStream(new Stream("B.out", "C.in")); 
            target.AddStream(new Stream("C.out", ".out"));

            target.AddOperation(new Operation("A.in", (input, outputCont) => outputCont(new Message("A.out", input.Data.ToString() + "+A"))));
            target.AddOperation(new Operation("B.in", (input, outputCont) => outputCont(new Message("B.out", input.Data.ToString() + "+B"))));
            target.AddOperation(new Operation("C.in", (input, outputCont) => outputCont(new Message("C.out", input.Data.ToString() + "+C"))));

            IMessage result = null;
            target.Result += output => result = output;

            target.Process(new Message(".in", "Test message"));

            Assert.AreEqual(".out", result.Port.Name);
            Assert.AreEqual("Test message+A+B+C", result.Data.ToString());
        }

        [TestMethod()]
        public void Output_with_fan_out()
        {
            var target = new FlowRuntime();

            target.AddStream(new Stream(".in", "A.in"));
            target.AddStream(new Stream(".in", "B.in"));
            target.AddStream(new Stream("A.out", ".out"));
            target.AddStream(new Stream("B.out", ".out"));

            target.AddOperation(new Operation("A.in", (input, outputCont) => outputCont(new Message("A.out", input.Data.ToString() + "+A"))));
            target.AddOperation(new Operation("B.in", (input, outputCont) => outputCont(new Message("B.out", input.Data.ToString() + "+B"))));

            var results = new List<IMessage>();
            target.Result += results.Add;

            target.Process(new Message(".in", "Test message"));

            Assert.AreEqual("Test message+A", results[0].Data);
            Assert.AreEqual("Test message+B", results[1].Data); 
        }
    }
}
